package com.ruoyi.util;

import io.minio.*;
import io.minio.http.Method;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.python.apache.xerces.util.URI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;


@Slf4j
@Component("customMinioUtil")  // 自定义名称
public class MinioUtil {

    @Autowired
    public MinioClient minioClient;

    /**
     * PUBLIC桶策略
     * 如果不配置，则新建的存储桶默认是PRIVATE，则存储桶文件会拒绝访问 Access Denied
     *
     * @param bucketName
     * @return
     */
    private static String publicBucketPolicy(String bucketName) {
        /**
         * AWS的S3存储桶策略
         * Principal: 生效用户对象
         * Resource:  指定存储桶
         * Action: 操作行为
         */
        String builder = "{\"Version\":\"2012-10-17\","
                + "\"Statement\":[{\"Effect\":\"Allow\","
                + "\"Principal\":{\"AWS\":[\"*\"]},"
                + "\"Action\":[\"s3:ListBucketMultipartUploads\",\"s3:GetBucketLocation\",\"s3:ListBucket\"],"
                + "\"Resource\":[\"arn:aws:s3:::" + bucketName + "\"]},"
                + "{\"Effect\":\"Allow\"," + "\"Principal\":{\"AWS\":[\"*\"]},"
                + "\"Action\":[\"s3:ListMultipartUploadParts\",\"s3:PutObject\",\"s3:AbortMultipartUpload\",\"s3:DeleteObject\",\"s3:GetObject\"],"
                + "\"Resource\":[\"arn:aws:s3:::" + bucketName + "/*\"]}]}";

        return builder;
    }

    /**
     * Minio文件上传
     *
     * @param file       文件实体
     * @param fileName   修饰过的文件名 非源文件名
     * @param bucketName 所存文件夹（桶名）
     * @return
     */
    public Map<String, String> minioUpload(MultipartFile file, String fileName, String bucketName) {
        InputStream inputStream = null;
        Map<String, String> resultEntity = new HashMap<>();
        try {
            // fileName为空，说明要使用源文件名上传
            if (fileName == null) {
                fileName = file.getOriginalFilename();
                fileName = fileName.replaceAll(" ", "_");
            }
            inputStream = file.getInputStream();
            PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(fileName)
                    .stream(inputStream, file.getSize(), -1).contentType(file.getContentType()).build();
            //文件名称相同会覆盖
            minioClient.putObject(objectArgs);
            String fileUrl = minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .bucket(bucketName)
                            .object(fileName)
                            .method(Method.GET)
                            .build());
            // url封装
            URI uri = new URI(fileUrl);
            String cleanUrl = uri.getScheme() + "://" + uri.getHost() + (uri.getPort() != -1 ? ":" + uri.getPort() : "") + uri.getPath();
            log.info("fileUrl: {}", cleanUrl);
            // 结果封装
            resultEntity.put("fileUrl", cleanUrl);
            resultEntity.put("fileName", fileName);
            resultEntity.put("success", "true");
            return resultEntity;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 检查存储桶是否存在
     *
     * @param bucketName 存储桶名称
     * @return
     */
    public boolean bucketExists(String bucketName) {
        try {
            return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取文件流
     *
     * @param fileName   文件名
     * @param bucketName 桶名（文件夹）
     * @return
     */
    public InputStream getFileInputStream(String fileName, String bucketName) {
        try {
            return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return null;
    }

    /**
     * 创建存储桶(存储桶不存在)
     *
     * @param bucketName
     */
    @SneakyThrows
    private void createBucketIfAbsent(String bucketName) {
        BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder().bucket(bucketName).build();
        if (!minioClient.bucketExists(bucketExistsArgs)) {
            MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder().bucket(bucketName).build();
            minioClient.makeBucket(makeBucketArgs);
            // 设置存储桶访问权限为PUBLIC， 如果不配置，则新建的存储桶默认是PRIVATE，则存储桶文件会拒绝访问 Access Denied
            SetBucketPolicyArgs setBucketPolicyArgs = SetBucketPolicyArgs
                    .builder()
                    .bucket(bucketName)
                    .config(publicBucketPolicy(bucketName))
                    .build();
            minioClient.setBucketPolicy(setBucketPolicyArgs);
        }
    }

    /**
     * 下载文件
     *
     * @param originalName 文件路径
     */
    public InputStream downloadFile(String bucketName, String originalName, HttpServletResponse response) {
        try {
            InputStream file = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(originalName).build());
            // 处理文件名编码，适配Jakarta
            String filename = new String(originalName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
            if (StringUtils.isNotBlank(originalName)) {
                filename = originalName;
            }
            response.setHeader("Content-Disposition", "attachment;filename=" + filename);
            ServletOutputStream servletOutputStream = response.getOutputStream();
            int len;
            byte[] buffer = new byte[1024];
            while ((len = file.read(buffer)) > 0) {
                servletOutputStream.write(buffer, 0, len);
            }
            servletOutputStream.flush();
            file.close();
            servletOutputStream.close();
            return file;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 删除桶
     * @param bucketName 存储桶名称
     */
    public void deleteBucketName(String bucketName) {
        try {
            if (StringUtils.isBlank(bucketName)) {
                return;
            }
            boolean isExist = bucketExists(bucketName);
            if (isExist) {
                minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
    }

    /**
     * 删除桶下面所有文件
     * @param bucketName 存储桶名称
     */
    public void deleteBucketFile(String bucketName) {
        try {
            if (StringUtils.isBlank(bucketName)) {
                return;
            }
            boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (isExist) {
                minioClient.deleteBucketEncryption(DeleteBucketEncryptionArgs.builder().bucket(bucketName).build());
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
    }

    /**
     * 删除文件
     */
    public boolean deleteFile(String bucketName, String fileName) {
        try {
            log.info("开始删除MinIO文件，bucketName: {}, fileName: {}", bucketName, fileName);

            // 检查文件是否存在
            try {
                minioClient.statObject(StatObjectArgs.builder()
                        .bucket(bucketName)
                        .object(fileName)
                        .build());
            } catch (Exception e) {
                log.warn("文件不存在，bucketName: {}, fileName: {}", bucketName, fileName);
                return true; // 如果文件不存在，认为删除成功
            }

            // 执行删除
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(bucketName)
                            .object(fileName)
                            .build()
            );
            log.info("MinIO文件删除成功，bucketName: {}, fileName: {}", bucketName, fileName);
            return true;
        } catch (Exception e) {
            log.error("MinIO文件删除失败，bucketName: {}, fileName: {}, 错误: {}", bucketName, fileName, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 根据文件路径得到预览文件绝对地址
     *
     * @param bucketName 存储桶名称
     * @param fileName 文件名
     * @return 预览URL
     */
    public String getPreviewFileUrl(String bucketName, String fileName) {
        try {
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(fileName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }

    /**
     * 上传文件流到MinIO
     *
     * @param bucketName  存储桶名称
     * @param fileName    文件名
     * @param inputStream 文件输入流
     * @param size        文件大小，-1表示未知
     * @param partSize    分片大小
     * @return 是否上传成功
     */
    public boolean putObject(String bucketName, String fileName, InputStream inputStream, long size, int partSize) {
        try {
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(fileName)
                            .stream(inputStream, size, partSize)
                            .build()
            );
            return true;
        } catch (Exception e) {
            log.error("上传文件到MinIO失败", e);
            return false;
        }
    }

    /**
     * 获取文件输入流
     * @param filePath 文件路径
     * @param bucketName 存储桶名称
     * @return 文件输入流
     */
    public InputStream getFileInputStreams(String filePath, String bucketName) {
        try {
            // 检查桶是否存在
            if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
                log.warn("MinIO 桶不存在: {}", bucketName);
                return null;
            }
            // 检查对象是否存在
            try {
                minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(filePath).build());
            } catch (Exception e) {
                log.warn("MinIO 文件不存在: {}/{}", bucketName, filePath);
                return null;
            }
            return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(filePath).build());
        } catch (Exception e) {
            log.error("获取文件输入流失败 桶：{}，文件路径{}",bucketName,filePath,e);
            return null;
        }
    }

    /**
     * 保存字符串内容到MinIO文件
     * @param bucketName 桶名
     * @param fileName 文件名
     * @param content 文件内容（字符串）
     * @return 是否保存成功
     */
    public boolean saveContent(String bucketName, String fileName, String content) {
        try (InputStream stream = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))) {
            createBucketIfAbsent(bucketName); // 确保存储桶存在
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(fileName)
                            .stream(stream, content.getBytes().length, -1)
                            .contentType("text/plain")
                            .build()
            );
            return true;
        } catch (Exception e) {
            log.error("保存内容到MinIO失败", e);
            return false;
        }
    }

    /**
     * 获取文件内容（新增方法）
     * @param bucketName 存储桶名称
     * @param objectName 文件名
     * @return 文件内容字符串
     */
    public String getContent(String bucketName, String objectName) {
        try (InputStream stream = minioClient.getObject(GetObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .build());
             ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {

            byte[] data = new byte[4096];
            int bytesRead;
            while ((bytesRead = stream.read(data, 0, data.length)) != -1) {
                buffer.write(data, 0, bytesRead);
            }

            return new String(buffer.toByteArray(), StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new RuntimeException("文件读取失败: " + e.getMessage());
        }
    }
}
